
from datetime import datetime, timedelta
from ferris import BasicModel
from google.appengine.ext import ndb
from app.models import util
from app.models.feature import Feature


class Issue(BasicModel):
    """
    Models issues of a publication. An issue usually has an issue number, and
    is published in a particular period (March, week 10, etc.) of a year. An
    issue can have a description of its cover, and links to a web page and a
    cover image.
    """
    publication = ndb.KeyProperty(kind="Publication", required=True)
    number = ndb.IntegerProperty(required=True)
    year = ndb.IntegerProperty(required=True)
    period = ndb.KeyProperty(kind="Period")
    cover = ndb.StringProperty()
    link = ndb.StringProperty()
    cover_image_link = ndb.StringProperty()
    publication_day = ndb.ComputedProperty(lambda self:
                                           self.get_publication_day())
    search_terms = ndb.ComputedProperty(lambda self:
                                        util.create_search_terms(self.cover),
                                        repeated=True)

    def get_publication_day(self):
        """
        Computes the publication day as an ordinal number from 1/1/1, to be
        used for sorting. The day is constructed from the 1st of January of the
        issue's publication year plus the period's order value if the period is
        set.
        """
        first_day = datetime(self.year, 1, 1)
        if self.period:
            first_day += timedelta(days = self.period.get().order - 1)
        return first_day.toordinal()

    def link_short(self):
        """
        Returns a shortened link URL.
        """
        return util.shorten_url(self.link) if self.link else None

    @classmethod
    def list_query(cls, query=None):
        """
        Queries all issues with an optional query string, ordered by the issue
        number.
        """
        if query:
            search_args = util.prepare_search_args(query)
            numbers = [int(n) for n in query.split() if n.isdigit()]
            if numbers:
                results = cls.query(ndb.OR(cls.search_terms.IN(search_args),
                                           cls.number.IN(numbers),
                                           cls.year.IN(numbers)))
            else:
                results = cls.query(cls.search_terms.IN(search_args))
        else:
            results = cls.query()

        return results.order(-cls.publication_day, cls.number, cls.key)

    def all_features(self):
        """
        List all features of an issue.
        """
        return Feature.query(Feature.issue == self.key).order(
            Feature.description_sort)

    def period_year(self):
        """
        Return a string formed by concatenating the period and the year of this
        issue.
        """
        if self.period:
            return str(self.period.get().name) + " " + str(self.year)
        else:
            return str(self.year)

    def number_period_year(self):
        """
        Return a string formed by concatenating the number, the period and the
        year.
        """
        return str(self.number) + " - " + self.period_year()

    def _post_put_hook(self, future):
        """
        Post put hook to force an update on each feature associated with this
        issue to make sure computed fields are kept up-to-date.
        """
        key = future.get_result()
        if key:
            query = Feature.query(Feature.issue == key)
            query.map(util.update_entity_async)

